package ru.ibrae.excelparser1.mainlogics.constructors;

import jxl.Cell;
import jxl.CellType;
import jxl.DateCell;
import jxl.LabelCell;
import jxl.NumberCell;
import jxl.Sheet;
import jxl.Workbook;

import jxl.read.biff.BiffException;

import ru.ibrae.excelparser1.excelstorageentities.constructionresults.*;
import ru.ibrae.excelparser1.excelstorageentities.entities.CertificateInfo;
import ru.ibrae.excelparser1.excelstorageentities.entities.DocumentInfo;
import ru.ibrae.excelparser1.excelstorageentities.entities.ExcelField;
import ru.ibrae.excelparser1.excelstorageentities.entities.ExcelFile;
import ru.ibrae.excelparser1.excelstorageentities.entities.InfoSheetHeader;
import ru.ibrae.excelparser1.excelstorageentities.entities.MchsLetter;
import ru.ibrae.excelparser1.excelstorageentities.entities.MessageLevel;
import ru.ibrae.excelparser1.excelstorageentities.entities.PersonInfo;
import ru.ibrae.excelparser1.excelstorageentities.enumerations.*;
import ru.ibrae.excelparser1.gui.Main;
import ru.ibrae.excelparser1.mainlogics.Logging.implimentation.*;
import ru.ibrae.excelparser1.mainlogics.fileworkers.FileNameParser.FileNameVerificaionResult;

import java.io.File;
import java.io.IOException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;

public class ExcelEntitiesConstructor {
    //~ Instance fields --------------------------------------------------------

    private final String COLUMN_NAMES = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    private ComplexLogger textAreaLogger = null;

    //~ Methods ----------------------------------------------------------------
    public ExcelFile construct(HashMap<Integer, Class> cellDescriptions,
            File fileToRead, ComplexLogger tal,
            FileNameVerificaionResult verificaionResult)
            throws IOException, BiffException {
        textAreaLogger = tal;

        Workbook workbook;

        try {
            workbook = Workbook.getWorkbook(fileToRead);
        }
        catch (Exception ex) {
            writeToLog("Не удается прочитать файл");

            return null;
        }

        Sheet workSheet = workbook.getSheet(
                1);
        CommonInfoConstructionResult commonInfoConstructionResult = null;
        int correctLastRow = 0;
        final int foundRows = workSheet.getRows();

        for (int i = 2; i < foundRows; i++) {
            try {
                Cell cell = workSheet.getCell(0, i);

                if (cell.getContents().equals("")) {
                    correctLastRow = i - 1;

                    break;
                }
            }
            catch (Throwable t) {
                correctLastRow = i;
            }
        }

        if (correctLastRow == 0) {
            correctLastRow = workSheet.getRows() - 1;
        }

        try {
            commonInfoConstructionResult = CommoinInfoConstructor.constructCommonInfo(
                    fileToRead,
                    correctLastRow,
                    verificaionResult);

            if (commonInfoConstructionResult.getVerificationResult().getMessageLevel() != MessageLevel.NO_ERRORS) {
                writeToLog(
                        commonInfoConstructionResult.getVerificationResult().getMessage());

                if (commonInfoConstructionResult.getVerificationResult().getMessageLevel() == MessageLevel.FILE_FATAL) {
                    return null;
                }
            }
        }
        catch (Exception ex) {
            writeToLog("Не удается прочитать файл");

            return null;
        }

        if (commonInfoConstructionResult.getVerificationResult().getMessageLevel() != MessageLevel.NO_ERRORS) {
            writeToLog(
                    commonInfoConstructionResult.getVerificationResult().getMessage());

            if (commonInfoConstructionResult.getVerificationResult().getMessageLevel() == MessageLevel.FILE_FATAL) {
                return null;
            }
        }

        ArrayList<Integer> notNullable = new ArrayList<Integer>(
                Arrays.asList(new Integer[]{0, 1, 2, 3, 13, 14, 15}));
        LinkedList<ExcelField> rows = new LinkedList<ExcelField>();

        for (int i = 2; i <= correctLastRow; i++) {
            HashMap<Integer, Object> rowMap = new HashMap<Integer, Object>();
            MessageLevel messageLevel = null;

            for (int j = 0; j < 21; j++) {
                final CellVerificationResult result = handleCell(workSheet,
                        j,
                        i,
                        notNullable,
                        cellDescriptions);
                rowMap.put(result.getColumn(), result.getContent());
                messageLevel = result.getVerificationResult().getMessageLevel();

                if (messageLevel != MessageLevel.NO_ERRORS) {
                    if (!result.getVerificationResult().getMessage().toLowerCase().startsWith("пустая")) {
                        writeToLog(result.getVerificationResult().getMessage());
                    }

                    if (messageLevel == MessageLevel.ROW_FATAL) {
                        Cell[] row = workSheet.getRow(i);
                        Cell[] newRow = new Cell[21];
                        System.arraycopy(row, 0, newRow, 0, 21);
                        Main.brokenExcelRows.add(newRow);
                        break;
                    }
                }
            }

            if (messageLevel == MessageLevel.ROW_FATAL) {
                continue;
            }

            CertificateInfo currCertificateInfo = new CertificateInfo();
            CertificateInfo previousCertificateInfo = new CertificateInfo();
            DocumentInfo documentInfo = new DocumentInfo();
            PersonInfo info = new PersonInfo();
            MchsLetter letter = new MchsLetter();
            ExcelField excelField = new ExcelField();
            excelField.setMchsLetter(letter);
            letter.setPerson(info);
            info.setDocumentInfo(documentInfo);
            info.setCurrentCertificate(currCertificateInfo);
            info.setPreviousCertificate(previousCertificateInfo);

            for (int j = 0; j < rowMap.size(); j++) {
                Object object = rowMap.get(j);

                switch (j) {
                    case 0:
                        excelField.setRowNumber(
                                Double.valueOf(object.toString()).intValue());

                        break;

                    case 1:
                        letter.setNumber(
                                ( object == null ) ? null : object.toString());

                        break;

                    case 2:
                        letter.setDate(( object == null ) ? null : (Date) object);

                        break;

                    case 3:

                        if (object == null) {
                            info.setSecondName(null);
                        } else {
                            String secondNames[] = object.toString().replaceAll("\\)", "").replaceAll(
                                    " ",
                                    "").split("\\(");

                            if (secondNames.length == 2) {
                                info.setOldSecondName(secondNames[1]);
                                info.setSecondName(secondNames[0]);
                            } else {
                                info.setSecondName(secondNames[0]);
                            }
                        }

                        break;

                    case 4:

                        if (object == null) {
                            info.setFirstName(null);
                        } else {
                            String firstNames[] = object.toString().replaceAll("\\)", "").replaceAll(
                                    " ",
                                    "").split("\\(");

                            if (firstNames.length == 2) {
                                info.setOldFirstName(firstNames[1]);
                                info.setFirstName(firstNames[0]);
                            } else {
                                info.setFirstName(firstNames[0]);
                            }
                        }

                        break;

                    case 5:

                        if (object == null) {
                            info.setSecondName(null);
                        } else {
                            String thirdNames[] = object.toString().replaceAll("\\)", "").replaceAll(
                                    " ",
                                    "").split("\\(");

                            if (thirdNames.length == 2) {
                                info.setOldThirdName(thirdNames[1]);
                                info.setThirdName(thirdNames[0]);
                            } else {
                                info.setThirdName(thirdNames[0]);
                            }
                        }

                        break;

                    case 6:
                        info.setBirthDate(( object == null ) ? null : (Date) object);

                        break;

                    case 7:
                        info.setAddress(
                                ( object == null ) ? null : object.toString());

                        break;

                    case 8:
                        documentInfo.setDocumentType(
                                getDocumentTypeByText(
                                ( object == null ) ? null : object.toString()));

                        break;

                    case 9:
                        documentInfo.setSeries(
                                ( object == null ) ? null : object.toString());

                        break;

                    case 10:
                        documentInfo.setNumber(
                                ( object == null ) ? null : object.toString());

                        break;

                    case 11:
                        documentInfo.setWhenGiven(
                                ( object == null ) ? null : (Date) object);

                        break;

                    case 12:
                        documentInfo.setWhereGiven(
                                ( object == null ) ? null : object.toString());

                        break;

                    case 13:
                        currCertificateInfo.setSeries(
                                ( object == null ) ? null : object.toString());

                        break;

                    case 14:
                        currCertificateInfo.setNumber(
                                ( object == null ) ? null : object.toString());

                        break;

                    case 15:
                        currCertificateInfo.setWhenGiven(
                                ( object == null ) ? null : (Date) object);

                        break;

                    case 16:
                        currCertificateInfo.setStatus(
                                getMarkByText(
                                ( object == null ) ? null : object.toString()));

                        break;

                    case 17:
                        previousCertificateInfo.setSeries(
                                ( object == null ) ? null : object.toString());

                        break;

                    case 18:
                        previousCertificateInfo.setNumber(
                                ( object == null ) ? null : object.toString());

                        break;

                    case 19:
                        previousCertificateInfo.setWhenGiven(
                                ( object == null ) ? null : (Date) object);

                        break;

                    case 20:
                        previousCertificateInfo.setStatus(
                                getMarkByText(
                                ( object == null ) ? null : object.toString()));
                }
            }

            boolean add = rows.add(excelField);
        }

        ExcelFile ef = new ExcelFile(commonInfoConstructionResult.getCommonInfo(),
                constructInfoSheetHeader(fileToRead),
                rows);

        return ef;
    }

    private NeededCellType getMyCellTypeByCellType(CellType cellType) {
        if (cellType == CellType.LABEL) {
            return NeededCellType.LABEL;
        } else if (cellType == CellType.NUMBER) {
            return NeededCellType.NUMBER;
        } else if (cellType == CellType.DATE) {
            return NeededCellType.DATE;
        } else {
            return NeededCellType.EMPTY;
        }
    }

    private Mark getMarkByText(String markText) {
        if (markText == null || "".equals(markText)) {
            return Mark.NO_MARK;
        }

        if (markText.equalsIgnoreCase("д")) {
            return Mark.D;
        } else if (markText.equalsIgnoreCase("п")) {
            return Mark.P;
        } else if (markText.equalsIgnoreCase("дп")) {
            return Mark.DP;
        }

        writeToLog(
                "Неправильный идентификатор удостоверения, распознается как пустой");

        return Mark.NO_MARK;
    }

    private DocumentType getDocumentTypeByText(String documentText) {
        if (documentText == null) {
            return null;
        } else {
            if (documentText.equalsIgnoreCase("п")) {
                return DocumentType.PASSPORT;
            } else if (documentText.equalsIgnoreCase("в")) {
                return DocumentType.MILITARY_ID;
            } else if (documentText.equalsIgnoreCase("у")) {
                return DocumentType.TEMP_CERTIFICATE;
            } else {
                return null;
            }
        }
    }

    private CellVerificationResult handleCell(Sheet workSheet, int col,
            int row,
            ArrayList<Integer> notNullableCols,
            HashMap<Integer, Class> cellDescriptions) {
        Cell currentCell = workSheet.getCell(col, row);
        String colName = COLUMN_NAMES.charAt(col)
                + new Integer(row + 1).toString();

        //System.out.println(currentCell.getType());
        VerificationResult result = new VerificationResult("",
                MessageLevel.NO_ERRORS);
        CellVerificationResult cellVerificationResult = new CellVerificationResult(
                null,
                col,
                null);

        if (!( currentCell.getContents().equals("")
                && notNullableCols.contains(col) )) {
            CellType cellType = currentCell.getType();
            Class neededClass = cellDescriptions.get(col);
            Class realClass = getMyCellTypeByCellType(cellType).getCellContentClass();

            if (realClass == neededClass) {
                if (realClass == Date.class) {
                    DateCell dc = (DateCell) currentCell;
                    cellVerificationResult.setContent(dc.getDate());
                } else if (realClass == Integer.class) {
                    NumberCell nc = (NumberCell) currentCell;
                    cellVerificationResult.setContent(nc.getValue());
                } else {
                    LabelCell lc = null;

                    if (!currentCell.getContents().equals("")) {
                        lc = (LabelCell) currentCell;
                    }

                    if (currentCell.getContents().equals("")) {
                        cellVerificationResult.setContent("");
                    } else {
                        cellVerificationResult.setContent(
                                lc.getString().replaceAll(" ", ""));
                    }
                }
            } else {
                if (( neededClass == String.class )
                        && ( realClass == Integer.class )) {
                    cellVerificationResult.setContent(currentCell.getContents());
                } else if (( neededClass == Integer.class )
                        && ( realClass == String.class )) {
                    LabelCell lc = (LabelCell) currentCell;
                    String string = lc.getString().replaceAll(" ", "");
                    Integer integer;

                    try {
                        integer = Integer.parseInt(string);
                        cellVerificationResult.setContent(integer);
                    }
                    catch (Exception e) {
                        result = new VerificationResult(
                                "Неправильный формат данных в ячейке " + colName
                                + "\nОбрабатывается как пустая",
                                MessageLevel.ROW_NON_FATAL);
                        cellVerificationResult.setVerificationResult(result);
                    }
                } else {
                    if (currentCell.getContents().equals("")) {
                        result.setMessage("Пустая ячейка " + colName);
                        result.setMessageLevel(MessageLevel.ROW_NON_FATAL);
                    } else {
                        result.setMessage(
                                "Невозможно преобразовать данные в ячейке "
                                + colName + "\nОбрабатывается как пустая");
                        result.setMessageLevel(MessageLevel.ROW_NON_FATAL);
                    }

                    cellVerificationResult.setContent("");
                }
            }
        } else {
            result.setMessageLevel(MessageLevel.ROW_FATAL);
            result.setMessage(
                    "Не заполнена обязательная для заполнения ячейка " + colName
                    + "\nОбработка строки прервана");
            cellVerificationResult.setVerificationResult(result);

            return cellVerificationResult;
        }

        cellVerificationResult.setVerificationResult(result);

        return cellVerificationResult;
    }

    private void writeToLog(String message) {
        Boolean logged = getTextAreaLogger().log(message);
    }

    private InfoSheetHeader constructInfoSheetHeader(File fileToRead)
            throws IOException, BiffException {
        Workbook w = null;
        w = Workbook.getWorkbook(fileToRead);

        Sheet s = w.getSheet(1);
        ArrayList<Cell> cells = new ArrayList<Cell>();

        for (int i = 0; i < 21; i++) {
            for (int j = 0; j < 2; j++) {
                Cell c = s.getCell(i, j);
                cells.add(c);
            }
        }

        InfoSheetHeader ish = new InfoSheetHeader(new LinkedList<Cell>(cells));

        return ish;
    }

    private ComplexLogger getTextAreaLogger() {
        if (textAreaLogger == null) {
            textAreaLogger = new ComplexLogger();
        }

        return textAreaLogger;
    }
}
